using SocialExplorer.IO.FastDBF;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;

namespace Glpt.Data
{
    /// <summary>
    /// DBF文件流类
    /// </summary>
    public class DbfDataReader : IDataReader
    {
        private DbfFile dbf = null;
        private Dictionary<string, int> columnsByName = new Dictionary<string, int>();
        private Dictionary<int, string> columnsByOrdinal = new Dictionary<int, string>();
        private Dictionary<int, Type> typesByOrdinal = new Dictionary<int, Type>();
        private Dictionary<string, string> colnames_ext = new Dictionary<string, string>();//附加列
        private int Cols_ext = 0; //附加列的个数
        private int Col_rowIndex = -1;//自增字段列序号
        private DbfRecord currentRow;
        private int ifirstIndex = 1;
        private int ilastIndex = int.MaxValue;
        private int irowIndex = 0;
        private int ithreadcount = 0;//线程数
        private int iremainder = -1;//行号余数
        private bool _isClosed = true;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="fileName">文件名</param>
        public DbfDataReader(string fileName)
        {
            Init(fileName);
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="param">参数</param>
        public DbfDataReader(string fileName, Dictionary<string, string> param)
        {
            if (param.ContainsKey("FIRSTROW") && param["FIRSTROW"].Length > 0)
            {
                int.TryParse(param["FIRSTROW"], out ifirstIndex);
                if (ifirstIndex < 1) ifirstIndex = 1;
            }
            if (param.ContainsKey("LASTROW") && param["LASTROW"].Length > 0)
            {
                int.TryParse(param["LASTROW"], out ilastIndex);
                if (ilastIndex < 1) ilastIndex = int.MaxValue;
            }
            if (param.ContainsKey("THREADCOUNT") && param["THREADCOUNT"].Length > 0)
                ithreadcount = int.Parse(param["THREADCOUNT"]);
            if (param.ContainsKey("REMAINDER") && param["REMAINDER"].Length > 0)
                iremainder = int.Parse(param["REMAINDER"]);
            foreach (string key in param.Keys)
            {
                if (key.StartsWith(":"))
                {
                    colnames_ext.Add(key.Substring(1), param[key]);
                }
            }
            Init(fileName);
        }

        private void Init(string fileName)
        {
            Cols_ext = 0;
            foreach (string key in colnames_ext.Keys)
            {
                columnsByName.Add(key, Cols_ext);
                columnsByOrdinal.Add(Cols_ext, key);
                if (colnames_ext[key].Equals("IDENTITY"))
                {
                    Col_rowIndex = Cols_ext;
                    typesByOrdinal.Add(Cols_ext, typeof(int));
                }
                else
                    typesByOrdinal.Add(Cols_ext, typeof(string));
                Cols_ext++;
            }
            dbf = new DbfFile(DataFuns.DefaultEncoding);
            dbf.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            for (int i = 0; i < dbf.Header.ColumnCount; i++)
            {
                string val = dbf.Header[i].Name;
                columnsByName.Add(val, i + Cols_ext);
                columnsByOrdinal.Add(i + Cols_ext, val);
                typesByOrdinal.Add(i + Cols_ext, DbfColumn.GetColumnType(dbf.Header[i].ColumnTypeChar));
            }
            currentRow = new DbfRecord(dbf.Header);
            while (irowIndex < ifirstIndex - 1)
            {
                irowIndex++;
                if (!dbf.ReadNext(currentRow))
                {
                    IsEOF = true;
                    break;
                }
            }
            _isClosed = false;
        }

        private bool IsEOF = false;

        public bool Read()
        {
            if (dbf == null || IsEOF) return false;
            else if (irowIndex >= ilastIndex) return false;
            if (!dbf.ReadNext(currentRow))
            {
                IsEOF = true;
                return false;
            }
            if (currentRow.IsDeleted)
                return Read();
            irowIndex++;
            bool isskip = (ithreadcount > 1 && iremainder >= 0 && irowIndex % ithreadcount != iremainder);
            if (isskip)
                return Read();
            else
                return true;
        }

        /// <summary>
        /// 关闭StreamReader对象和基础流
        /// </summary>
        public void Close()
        {
            if (dbf != null) dbf.Close();
            _isClosed = true;
        }

        public int FieldCount
        {
            get { return columnsByName.Count; }
        }
        public string GetString(int i)
        {
            if (i == Col_rowIndex)
                return irowIndex.ToString();
            else if (i < Cols_ext)
                return colnames_ext[columnsByOrdinal[i]];
            else
                return currentRow[i - Cols_ext];
        }

        public object GetValue(int i)
        {
            if (i == Col_rowIndex)
                return irowIndex;
            else if (i < Cols_ext)
                return colnames_ext[columnsByOrdinal[i]];
            else
                return currentRow.GetFieldValue(i - Cols_ext);
        }

        public string GetName(int i)
        {
            return columnsByOrdinal[i];
        }

        public int GetOrdinal(string name)
        {
            return columnsByName[name];
        }

        public int Depth
        {
            get
            {
                return 0;
            }
        }

        public bool IsClosed
        {
            get
            {
                return _isClosed;
            }
        }

        public int RecordsAffected
        {
            get
            {
                return -1;
            }
        }

        public DataTable GetSchemaTable()
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("ColumnName", typeof(string));
            dt.Columns.Add("ColumnOrdinal", typeof(int));
            dt.Columns.Add("ColumnSize", typeof(int)).DefaultValue = 255;
            dt.Columns.Add("NumericPrecision", typeof(short)).DefaultValue = 255;
            dt.Columns.Add("NumericScale", typeof(short)).DefaultValue = 255;
            dt.Columns.Add("IsUnique", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsKey", typeof(bool));
            dt.Columns.Add("BaseServerName", typeof(string));
            dt.Columns.Add("BaseCatalogName", typeof(string));
            dt.Columns.Add("BaseColumnName", typeof(string));
            dt.Columns.Add("BaseSchemaName", typeof(string));
            dt.Columns.Add("BaseTableName", typeof(string));
            dt.Columns.Add("DataType", typeof(System.Type));
            dt.Columns.Add("AllowDBNull", typeof(bool)).DefaultValue = true;
            dt.Columns.Add("ProviderType", typeof(int)).DefaultValue = 22;
            dt.Columns.Add("IsAliased", typeof(bool));
            dt.Columns.Add("IsExpression", typeof(bool));
            dt.Columns.Add("IsIdentity", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsAutoIncrement", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsRowVersion", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsHidden", typeof(bool));
            dt.Columns.Add("IsLong", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsReadOnly", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("ProviderSpecificDataType", typeof(System.Type));
            dt.Columns.Add("XmlSchemaCollectionDatabase", typeof(string));
            dt.Columns.Add("XmlSchemaCollectionOwningSchema", typeof(string));
            dt.Columns.Add("XmlSchemaCollectionName", typeof(string));
            dt.Columns.Add("UdtAssemblyQualifiedName", typeof(string)).DefaultValue = 22;
            dt.Columns.Add("NonVersionedProviderType", typeof(int));
            dt.Columns.Add("IsColumnSet", typeof(bool)).DefaultValue = false;
            for (int i = 0; i < FieldCount; i++)
            {
                DataRow dr = dt.NewRow();
                dr["ColumnName"] = GetName(i);
                dr["ColumnOrdinal"] = i;
                if (i >= Cols_ext)
                {
                    int length = dbf.Header[i - Cols_ext].Length;
                    int precision = dbf.Header[i - Cols_ext].DecimalCount;
                    dr["ColumnSize"] = length;
                    dr["NumericPrecision"] = length;
                    dr["NumericScale"] = precision;
                }
                dr["BaseColumnName"] = GetName(i);
                dr["DataType"] = GetFieldType(i);
                dr["ProviderSpecificDataType"] = GetFieldType(i);

                dt.Rows.Add(dr);
            }
            return dt;
        }

        public bool NextResult()
        {
            return false;
        }

        public object this[int i]
        {
            get
            {
                return GetValue(i);
            }
        }
        public object this[string name]
        {
            get
            {
                return GetValue(columnsByName[name]);
            }
        }
        public bool GetBoolean(int i)
        {
            return bool.Parse(GetString(i));
        }
        public byte GetByte(int i)
        {
            return byte.Parse(GetString(i));
        }
        public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
        {
            return -1;
        }
        public char GetChar(int i)
        {
            return char.Parse(GetString(i));
        }
        public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
        {
            return -1;
        }
        public IDataReader GetData(int i)
        {
            return null;
        }
        public string GetDataTypeName(int i)
        {
            return GetFieldType(i).Name;
        }
        public DateTime GetDateTime(int i)
        {
            return DateTime.Parse(GetString(i));
        }
        public decimal GetDecimal(int i)
        {
            return decimal.Parse(GetString(i));
        }
        public double GetDouble(int i)
        {
            return double.Parse(GetString(i));
        }
        public Type GetFieldType(int i)
        {
            return typesByOrdinal[i];
        }
        public float GetFloat(int i)
        {
            return float.Parse(GetString(i));
        }
        public Guid GetGuid(int i)
        {
            return Guid.Parse(GetString(i));
        }
        public short GetInt16(int i)
        {
            return short.Parse(GetString(i));
        }
        public int GetInt32(int i)
        {
            return int.Parse(GetString(i));
        }
        public long GetInt64(int i)
        {
            return long.Parse(GetString(i));
        }
        public int GetValues(object[] values)
        {
            for (int i = 0; i < columnsByName.Count; i++)
                values[i] = GetValue(i);
            return columnsByName.Count;
        }
        public bool IsDBNull(int i)
        {
            return (GetValue(i) == DBNull.Value);
        }
        public void Dispose()
        {
            try
            {
                GC.Collect();
            }
            catch { }
        }
        ~DbfDataReader()
        {
            Dispose();
        }
    }
}
